package com.k8.common.aop.internal.invocation;

import com.k8.common.aop.internal.interceptor.MethodInterceptor;
import com.k8.common.aop.internal.joinpoint.JoinPoint;
import com.k8.common.aop.internal.joinpoint.JoinPointImp;
import com.k8.common.util.AopUtil;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
 * around需要特殊处理，目标方法调用时可能调用别的方法对localthread产生影响，methodInvocation发生变动
 * */
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    private final Object proxy;

    private final Object target;
    private final Method method;
    private final MethodProxy methodProxy;

    private Object[] arguments;
    private JoinPoint joinPoint;
    private final Class<?> targetClass;

    private Map<String, Object> userAttributes;//注入调用方法的参数，方便后续传入切面方法
    private final List<MethodInterceptor> interceptorsAndDynamicMethodMatchers;
    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<MethodInterceptor> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = method;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
        int index = 0;
        for (Parameter parameter : method.getParameters()) {
            setUserAttribute(parameter.getName(), arguments[index++]);
        }
        String signature = target.getClass().getName() + "." + method.getName();
        this.joinPoint = new JoinPointImp(signature, proxy, target, arguments);
        this.methodProxy = methodProxy;
    }


    public final Object getProxy() {
        return this.proxy;
    }


    public final Object getThis() {
        return this.target;
    }

    public final AccessibleObject getStaticPart() {
        return this.method;
    }

    public final Method getMethod() {
        return this.method;
    }

    public final Object[] getArguments() {
        return this.arguments;
    }

    public void setArguments(Object... arguments) {
        this.arguments = arguments;
    }

    /*
     * 不提供根据参数动态选择拦截器
     * */
    public Object proceed() throws Throwable {
        if (currentInterceptorIndex == interceptorsAndDynamicMethodMatchers.size() - 1) {
            AopUtil.makeAccessible(method);
            return invoke0();
        } else {
            MethodInterceptor methodInterceptor = interceptorsAndDynamicMethodMatchers.get(++currentInterceptorIndex);
            return methodInterceptor.invoke(this);
        }
    }

    /**
     * 如果是通过javassist来进行优化代理的执行，直接修改字节码，使通过target直接进行调用
     */
    protected Object invoke0() throws Throwable {
        if (methodProxy != null) return methodProxy.invoke(proxy, arguments);
        return method.invoke(target, arguments);
    }


    public MethodInvocation invocableClone() {
        Object[] cloneArguments = this.arguments;
        if (this.arguments.length > 0) {
            cloneArguments = (Object[]) this.arguments.clone();
        }

        return this.invocableClone(cloneArguments);
    }

    public MethodInvocation invocableClone(Object... arguments) {
        if (this.userAttributes == null) {
            this.userAttributes = new HashMap();
        }

        try {
            ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) this.clone();
            clone.arguments = arguments;
            return clone;
        } catch (CloneNotSupportedException var3) {
            Class var10002 = this.getClass();
            throw new IllegalStateException("Should be able to clone object of type [" + var10002 + "]: " + var3);
        }
    }

    public void setUserAttribute(String key, Object value) {
        if (value != null) {
            if (this.userAttributes == null) {
                this.userAttributes = new HashMap();
            }

            this.userAttributes.put(key, value);
        } else if (this.userAttributes != null) {
            this.userAttributes.remove(key);
        }

    }


    public Object getUserAttribute(String key) {
        return this.userAttributes != null ? this.userAttributes.get(key) : null;
    }

    public Map<String, Object> getUserAttributes() {
        if (this.userAttributes == null) {
            this.userAttributes = new HashMap();
        }

        return this.userAttributes;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
        sb.append(this.method).append("; ");
        if (this.target == null) {
            sb.append("target is null");
        } else {
            sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
        }

        return sb.toString();
    }

    public JoinPoint getJoinPoint() {
        return joinPoint;
    }
}
